feat(core): add Application.run() to invoke registered commands programmatically#144
Open
bedus-creation wants to merge 6 commits into
Open
feat(core): add Application.run() to invoke registered commands programmatically#144bedus-creation wants to merge 6 commits into
bedus-creation wants to merge 6 commits into
Conversation
…ammatically
Expose a public run() method on the core Application that executes an
already-registered console command by name from Python, without going
through sys.argv, and returns the command exit code.
app.run("db:migrate")
app.run("db:seed", "--force")
app.run("db:seed", ["--force"])
Reuses the existing ConsoleApplication wiring (mirrors handle_command),
dispatching via Cleo with a constructed StringInput and auto_exit disabled
so the code is returned rather than terminating the host process.
bedus-creation
commented
Jun 29, 2026
bedus-creation
left a comment
Contributor
Author
There was a problem hiding this comment.
Code Review verdict: APPROVE (posting as a comment — GitHub blocks formal self-approval since the authenticated account authored this PR). Verified locally on branch task/application-run-method.
This is the correct deliverable (programmatic Application.run(), superseding the CLI-command approach in #143).
Review against the requested dimensions:
- Clean addition reusing console wiring (mirrors handle_command) ✅ —
run()buildsConsoleApplication(self)exactly likehandle_command(), then dispatches via Cleo with an explicitStringInput(nosys.argv). - No unintended core abstraction changes ✅ — purely additive: one
import shlex+ one new method inapplication.py. No existing lines modified; no Container/Provider/Model changes. - Correct auto_exit handling ✅ —
console.auto_exits(False)is the correct Cleo API; verified the exit code is returned, notsys.exit-ed. - Catchable unknown-command error ✅ —
app.run("does:not:exist")renders a clean Cleo error and returns exit code 1; noSystemExit, host process not terminated. Verified empirically. - Meaningful tests ✅ — 5 tests (zero exit, non-zero propagation, no-args, string args, list args); fixture restores the previous
Containersingleton on teardown. All pass; full console+core suite (126 tests) green; ruff clean. - Focused diff ✅ — 3 files, no
uv.lock, no version bumps.
Non-blocking nit (optional): consider one test pinning the unknown-command path (returns non-zero, no SystemExit), since that's currently verified only manually.
Holding merge until Console QA reports PASS on task #481, per PM.
Add cases for exit-code type, unknown command, None/empty args, short options, list/tuple args, space-preserving and stringified values, and repeat invocation.
Resolve the command via ConsoleApplication.find() before dispatch so an unknown name raises CleoCommandNotFoundError that callers can catch, instead of being swallowed into exit code 1 by Cleo's non-auto-exit run loop. Known-command behavior (returned exit code, no process exit) is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a public
run()method to the coreApplicationclass that executes an already-registered console command by name from Python code — a programmatic API, not a new artisan CLI command. Returns the command's exit code.Implementation
Application.run(command: str, args: str | list[str] | None = None) -> intinapplication.py.handle_command()by bootingConsoleApplication(self).StringInput(nosys.argv), and disablesauto_exitso the exit code is returned instead of terminating the host process.argsaccepts a string or a list/tuple (joined withshlex.join);Noneruns the command with no extra arguments.Tests
tests/console/test_application_run.py— registers a dummy command on a freshApplicationand covers:"hello --force")["hello", "--force"])The fixture restores the previous container singleton on teardown. All 5 pass; console/core/serve suites (144 tests) remain green. Verified end-to-end against the real
provider:publishcommand.Context
Supersedes the closed PR #143, which added a CLI
runcommand — the wrong interpretation. This delivers the intended programmatic API instead.